#include "xmlserializer.h"


#include <QDomAttr>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>
#include <QStringList>


XmlSerializer::XmlSerializer(QObject *parent) :
    AbstractSerializer(parent)
{
}


void XmlSerializer::serialize(QObject* source, QIODevice *output)
{
    if (!output) return;

    QDomDocument doc("ItemXml"); // this is not serialized?

    QDomElement root = toXml(source, doc);
    if (root.isNull()) return;

    doc.appendChild(root);

    QByteArray arr = doc.toByteArray(4);

    output->write(arr);
}


QObject* XmlSerializer::deserialize(QIODevice* input)
{
    m_error = "";

    QDomDocument doc;

    QString msg;
    int line = 0;
    int col = 0;

    if (!doc.setContent(input, &msg, &line, &col))
    {
        m_error = QString("%1 %2 %3").arg(msg).arg(line).arg(col);
        qDebug() << Q_FUNC_INFO << "Error setting document content!" << msg << line << col;
        return 0;
    }

    QDomElement root = doc.documentElement();
    QObject* ret = fromXmlElem(root, 0);


    return ret;
}

QString XmlSerializer::error() const
{
    return m_error;
}

QDomElement XmlSerializer::toXml(QObject *source, QDomDocument doc)
{
    if (!source) return QDomElement();

    QDomElement elem = toXmlElem(source, doc);

    // Add child items respectively
    foreach (QObject* item, source->children())
    {
        QDomElement child = toXml(item, doc);
        if (child.isNull()) continue;

        elem.appendChild(child);
    }

    return elem;
}

QObject *XmlSerializer::fromXml(QDomElement elem, QObject *parent)
{
    Q_UNUSED(elem)
    Q_UNUSED(parent)
    return 0;
}


QDomElement XmlSerializer::toXmlElem(QObject *source, QDomDocument doc)
{
    if (!source) return QDomElement();

    const QMetaObject* metaObj = source->metaObject();

    QString tag = metaObj->className();
    QDomElement elem = doc.createElement(tag);

    // Add static properties, which are defined with "Q_PROPERTY"
    for (int index = 0; index < metaObj->propertyCount(); index++)
    {
        QMetaProperty metaProp = metaObj->property(index);
        if (!metaProp.isWritable())
        {
            continue;
        }

        QString name = metaProp.name();

        if (name.isEmpty())
        {
            continue;
        }

        QVariant data = metaProp.read(source);
        if (data.isNull())
        {
            continue;
        }

        QString text = data.toString();
        if (text.isEmpty())
        {
            continue;
        }

        elem.setAttribute(name, text);
    }

    // Add dynamic properties, which have been set with QObject::setProperty().
    foreach (QByteArray name, source->dynamicPropertyNames())
    {
        QVariant data = source->property(name);
        if (data.isNull()) continue;

        QString text = data.toString();

        if (text.isEmpty())
        {
            continue;
        }

        elem.setAttribute(name, text);
    }


    return elem;
}


QObject* XmlSerializer::fromXmlElem(QDomElement source, QObject* parent)
{
    QString name = source.tagName();

    int type = QMetaType::type(name.toUtf8());

    if (type == QMetaType::UnknownType)
    {
        name = name + "*";
        type = QMetaType::type(name.toUtf8());
    }

    if (type == QMetaType::UnknownType)
    {
        m_error = QString("%1 is not a registered meta type!");
        return 0;
    }

    const QMetaObject* mo = QMetaType::metaObjectForType(type);
    if (!mo)
    {
        qWarning() << Q_FUNC_INFO << "Meta object is null for:" << QMetaType::typeName(type);
        return 0;
    }

    QObject* item = mo->newInstance();
    if (!item)
    {
        qWarning() << Q_FUNC_INFO << "Can't instantiate" << name << "- did you remember to add Q_INVOKABLE to default constructor?";
    }
    item->setParent(parent);
    const QMetaObject* metaObj = item->metaObject();


    // get item content
    QDomNamedNodeMap named_nodes = source.attributes();
    for (int i = 0; i < named_nodes.count(); i++)
    {
        QDomAttr attribute = named_nodes.item(i).toAttr();
        if (attribute.isNull()) continue;

        QVariant var = attribute.value();

        int index = metaObj->indexOfProperty(attribute.name().toUtf8());
        if (index < 0)
        {
            item->setProperty(attribute.name().toUtf8(), var);
            continue;
        }

        // static property defined with Q_PROPERTY
        QMetaProperty metaProp = metaObj->property(index);

        if (metaProp.isEnumType())
        {
            metaProp.write(item, qvariant_cast<int>(var));
            continue;
        }

        metaProp.write(item, var);

    }

    for (QDomElement elem = source.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        QObject* child = fromXmlElem(elem, item);
        if (!child) continue;

    }

    return item;
}


